﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Text;

using zijian666.Abstractions;
using zijian666.Core.Abstractions;

namespace zijian666.Core;

/// <summary>
/// 转换设置选项
/// </summary>
public class ConvertSettings : IConvertSettings, ISlot<IStringSerializer>, ISlot<IReflectCompiler>
{
    /// <summary>
    /// 转换程序编辑器
    /// </summary>
    private readonly ConvertorBuilder? _builder;

    /// <summary>
    /// 缓存已经编译好的转换程序
    /// </summary>
    private readonly ConcurrentDictionary<Type, object>? _convertors;

    /// <summary>
    /// 为 <see cref="GetToStringArgument(Type)"/> 提供返回值
    /// </summary>
    private Dictionary<Type, ToStringArgument>? _formats;

    /// <summary>
    /// 翻译器
    /// </summary>
    private List<ITranslator>? _translators;

    /// <summary>
    /// 字符串序列化程序
    /// </summary>
    private IStringSerializer? _stringSerializer;

    /// <summary>
    /// 构造函数
    /// </summary>
    public ConvertSettings(ConvertorBuilder builder)
    {
        _builder = builder ?? throw new ArgumentNullException(nameof(builder));
        _convertors = _builder is null ? null : new ConcurrentDictionary<Type, object>();
    }

    /// <summary>
    /// 构造函数
    /// </summary>
    public ConvertSettings()
    {
        _builder = null;
        _convertors = null;
    }

    /// <inheritdoc/>
    public IConvertor<T>? GetConvertor<T>(IConvertContext context)
        => (IConvertor<T>?)_convertors?.GetOrAdd(typeof(T), x => _builder!.Build<T>());

    /// <inheritdoc/>
    public List<ITranslator> Translators => _translators ??= new();

    /// <inheritdoc/>
    public IStringSerializer? StringSerializer
        => _stringSerializer ?? Converts.StringSerializers[Protocol] ?? Converts.StringSerializers.Default;

    /// <inheritdoc/>
    public CultureInfo? CultureInfo { get; set; }

    /// <inheritdoc/>
    public NumberFormatInfo? NumberFormatInfo { get; set; }

    /// <inheritdoc/>
    public Encoding? Encoding { get; set; }

    /// <inheritdoc/>
    public StringSeparator? StringSeparator { get; set; }

    /// <inheritdoc/>
    public StringSplitOptions StringSplitOptions { get; set; }

    /// <inheritdoc/>
    public IReflectCompiler? ReflectCompiler { get; set; }

    /// <inheritdoc/>
    void ISlot<IStringSerializer>.Set(IStringSerializer feature)
        => _stringSerializer = feature;

    /// <inheritdoc/>
    void ISlot<IReflectCompiler>.Set(IReflectCompiler feature)
        => ReflectCompiler = CachedReflectCompiler.Cached(feature);

    /// <inheritdoc/>
    IEnumerable<ITranslator>? IConvertSettings.Translators => _translators;

    /// <inheritdoc/>
    public string Protocol { get; set; } = StringSerializerCollection.DefaultProtocol;

    /// <inheritdoc/>
    public bool StrictEnum { get; set; } = true;
    /// <inheritdoc/>
    public ToStringArgument? GetToStringArgument(Type type)
    {
        if (_formats is not null && _formats.TryGetValue(type, out var arg))
        {
            return arg;
        }
        return null;
    }

    /// <summary>
    /// 设置 <paramref name="type"/> 转为字符串时使用的参数
    /// </summary>
    public ConvertSettings SetToStringArgument(Type type, string? formatString, IFormatProvider? formatProvider)
    {
        if (type is null)
        {
            throw new ArgumentNullException(nameof(type));
        }

        if (formatString is null && formatProvider is null)
        {
            return this;
        }
        if (_formats is null)
        {
            _formats = new();
        }
        _formats[type] = new(formatString, formatProvider);
        return this;
    }
    /// <summary>
    /// 设置 <paramref name="type"/> 转为字符串时使用的参数
    /// </summary>
    public ConvertSettings SetToStringArgument(Type type, IFormatProvider formatProvider) => SetToStringArgument(type, null, formatProvider);
    /// <summary>
    /// 设置 <paramref name="type"/> 转为字符串时使用的参数
    /// </summary>
    public ConvertSettings SetToStringArgument(Type type, string formatString) => SetToStringArgument(type, formatString, null);
}
